/*
 * SHOPTNT 版权所有。
 * 未经许可，您不得使用此文件。
 * 官方地址：www.shoptnt.cn
 */
package cn.shoptnt.service.payment.impl;

import cn.shoptnt.framework.cache.Cache;
import cn.shoptnt.model.base.CachePrefix;
import cn.shoptnt.model.payment.dos.PaymentBillDO;
import cn.shoptnt.model.payment.dos.PaymentMethodDO;
import cn.shoptnt.model.payment.dto.PayParam;
import cn.shoptnt.model.payment.enums.ClientType;
import cn.shoptnt.model.payment.vo.PayBill;
import cn.shoptnt.model.payment.vo.PaymentMethodVO;
import cn.shoptnt.service.payment.PaymentBillManager;
import cn.shoptnt.service.payment.PaymentManager;
import cn.shoptnt.service.payment.PaymentMethodManager;
import cn.shoptnt.service.payment.PaymentPluginManager;
import cn.shoptnt.model.trade.order.enums.TradeTypeEnum;
import cn.shoptnt.framework.logs.Debugger;
import cn.shoptnt.framework.util.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 支付账单管理实现
 *
 * @author 妙贤
 * @version 1.0
 * @since 7.1.0
 * 2019-04-10
 */
@Service
public class PaymentManagerImpl implements PaymentManager {

    private final Logger logger = LoggerFactory.getLogger(getClass());


    @Autowired
    private PaymentBillManager paymentBillManager;

    @Autowired
    private List<PaymentPluginManager> paymentPluginList;

    @Autowired
    private Debugger debugger;

    @Autowired
    private PaymentMethodManager paymentMethodManager;

    @Autowired
    private Cache cache;


    @Override
    public Map pay(PayBill bill) {
        //调起相应的支付插件
        PaymentPluginManager paymentPlugin = this.findPlugin(bill.getPluginId());
        logger.debug("开始调起支付插件：" + bill.getPluginId());
        debugger.log("开始调起支付插件：" + bill.getPluginId());
        return paymentPlugin.pay(bill);
    }

    @Override
    public String payCallback(String paymentPluginId, ClientType clientType) {
        PaymentPluginManager plugin = this.findPlugin(paymentPluginId);
        if (plugin != null) {
            return plugin.onCallback(clientType);
        }
        return "fail";
    }

    @Override
    public List<PaymentMethodVO> queryPayments(String clientType) {
        List<PaymentMethodVO> list = paymentMethodManager.queryMethodByClient(clientType);
        return list;
    }

    @Override
    public void payReturn(TradeTypeEnum tradeType, String paymentPluginId) {
        PaymentPluginManager plugin = this.findPlugin(paymentPluginId);
        if (plugin != null) {
            plugin.onReturn(tradeType);
        }
    }

    @Override
    public String queryResult(String subSn, String serviceType) {

        //使用子订单号和业务类型查询账单bill
        PaymentBillDO paymentBill = paymentBillManager.getBySubSnAndServiceType(subSn, serviceType);

        //已经支付回调，则不需要查询
        if (paymentBill.getIsPay() == 1) {
            return "success";
        }

        PaymentPluginManager plugin = this.findPlugin(paymentBill.getPaymentPluginId());


        Map map = JsonUtil.jsonToObject(paymentBill.getPayConfig(), Map.class);
        List<Map> list = (List<Map>) map.get("config_list");

        Map<String, String> result = new HashMap<>(list.size());
        if (list != null) {
            for (Map item : list) {
                result.put(item.get("name").toString(), item.get("value").toString());
            }
        }

        return plugin.onQuery(paymentBill.getBillSn(), result);
    }

    @Override
    public Map pay(PayParam param) {

        //获取支付参数
        PaymentMethodDO paymentMethod = this.paymentMethodManager.getByPluginId(param.getPaymentPluginId());

        //组装bill信息
        PaymentBillDO tempBill = new PaymentBillDO(param.getSn(), param.getPrice(), param.getTradeType());
        tempBill.setPaymentPluginId(paymentMethod.getPluginId());
        tempBill.setPaymentName(paymentMethod.getMethodName());
        //保存支付参数
        ClientType clientType = ClientType.valueOf(param.getClientType());
        switch (clientType) {
            case PC:
                tempBill.setPayConfig(paymentMethod.getPcConfig());
                break;
            case WAP:
                tempBill.setPayConfig(paymentMethod.getWapConfig());
                break;
            case NATIVE:
                tempBill.setPayConfig(paymentMethod.getAppNativeConfig());
                break;
            case REACT:
                tempBill.setPayConfig(paymentMethod.getAppReactConfig());
                break;
            case MINI:
                tempBill.setPayConfig(paymentMethod.getMiniConfig());
                break;
            default:
                break;
        }
        //***************************************************************
        //预支付调起，保证幂等性：多次调起同一个订单，通过缓存隔离，对现有数据造成同样的影响，
        // 而不是每次影响一次数据（如bill、payment_method等）
        // 应对场景如下：
        // 1、打开多个客户端，都调起支付
        // 2、一个客户端先行支付，另外一个继续完成支付
        // 3、如果没有缓存隔离，第一个客户端会报错，因为此时账单表sn已经更换，但这个sn已经传给第三方支付平台，作为支付必要依据了
        //***************************************************************
        //获取缓存中的预支付信息key
        String key = this.getBillKey(param);
        //如果缓存中存在预支付信息，则直接返回
        Map map = (Map) cache.get(key);
        if (map != null) {
            logger.info("缓存中存在预支付信息{}", map);
            return map;
        }

        //判断子订单是否已经创建过相关的账单，如果价格更改过，则作废原来的账单
        PayBill bill = paymentBillManager.add(tempBill);
        bill.setPayMode(param.getPayMode());
        bill.setClientType(ClientType.valueOf(param.getClientType()));

        map = this.pay(bill);
        map.put("payment_method", paymentMethod.getMethodName());
        //将预支付信息放入缓存 并设置有效期为1小时
        cache.put(key, map, 60 * 60);
        return map;
    }

    /**
     * 获取缓存中的预支付信息key
     * @param param 支付参数
     * @return
     */
    private String getBillKey(PayParam param) {
        return CachePrefix.PAY_BILL_PREFIX.getPrefix()
                + param.getSn()
                + param.getTradeType()
                + param.getClientType()
                + param.getPaymentPluginId()
                + param.getPrice();
    }


    /**
     * 查找支付插件
     *
     * @param pluginId
     * @return
     */
    private PaymentPluginManager findPlugin(String pluginId) {
        for (PaymentPluginManager plugin : paymentPluginList) {
            if (plugin.getPluginId().equals(pluginId)) {
                return plugin;
            }
        }
        return null;
    }


}
